
module PNEngine
  class TransitionMode < ActiveRecord::Base
    include Resource::InstanceMethods
    extend Resource::ClassMethods
    
    #many-to-many
    has_and_belongs_to_many :input_tokens, :class_name => "Token"
    #a token can only be created by one mode -> one-to-many relationship
    has_many :output_tokens, :class_name => "Token"
    belongs_to :transition
    has_and_belongs_to_many :preceding_modes, :class_name => "TransitionMode",
      :association_foreign_key => "preceding_mode_id"
    has_and_belongs_to_many :succeeding_modes, :class_name => "TransitionMode",
      :foreign_key => "preceding_mode_id"
    belongs_to :execution_container, :polymorphic => true
    
    private :preceding_modes= #:preceding_modes,
    
    #returns an array of transition modes that are direct predecessors of self
    def get_preceding_modes
      if self.preceding_modes.nil?
        preceding_modes = find_preceding_modes
      elsif self.preceding_modes.empty?
        if !input_tokens.empty?
          #when no input tokens were consumed there can't be preceding modes
          # => don't need to find them
          modes = find_preceding_modes
          self.preceding_modes = modes
          #p "Found the following modes: #{modes.collect{|m| m.id}}"
        end
      end
      self.preceding_modes
    end
    
    # reverts the effects the mode's transition had, when it fired
    # deactivates all output tokens and activates all input tokens
    def undo
      if undo_possible?
        output_tokens.each{|token| token.set_active(false)}
        #input_tokens.each{|token| p "Before input each id:#{token.id}, Mode:#{token.creator_transition_mode.id}"}
        input_tokens.each{|token| 
          #p "Before set_active id:#{token.id}, Mode:#{token.creator_transition_mode.id}"
          token = PNEngine::Token.find(token.id) #dirty fix for weird activerecord bug
          token.set_active(true)}
        #move self into execution context
        self.execution_container = self.execution_container.case.execution_context
        save!
        p "Reversed transition mode: #{id}"
      end
    end
    
    def redo
      if redo_possible?
        output_tokens.each{|token| token.set_active(true)}
        input_tokens.each do |token| 
          token = PNEngine::Token.find(token.id) #dirty fix for weird activerecord bug
          arc = Arc.find(:first, :conditions => ["place_id = ? AND transition_id = ?",token.place.id, transition.id])
          token.set_active(false) unless arc.kind_of?(ReadArc)
        end
        #add to old execution log
        self.execution_container = self.execution_container.case.execution_log
        save!
        p "Restored transition mode: #{id}"
      end
    end
    
    def undo_possible?
      #all output tokens active
      # TODO maybe use .include?...include?(true)
      return false if output_tokens.collect{|token| token.is_active}.include?(false)
      #all input_tokens inactive, except ones with a read arc
      input = input_tokens.collect do |token|
        arc = Arc.find(:first, :conditions => ["place_id = ? AND transition_id = ?",token.place.id, transition.id])
        !token.is_active || arc.kind_of?(ReadArc)
      end
      return !input.include?(false)
    end
    
    def redo_possible?
      #all output tokens inactive
      return false if output_tokens.collect{|token| token.is_active}.include?(true)
      #all input tokens active
      return !input_tokens.collect{|token| token.is_active}.include?(false)
    end
    
    def to_xml
      <<-XML
       <mode>
        <id>#{id}</id>
        <execution_container_id>#{execution_container_id}</execution_container_id>
        <execution_container_type>#{execution_container_type}</execution_container_type>
        <transition_id>#{transition.id}</transition_id>
        <input_token_ids>#{input_tokens.collect{|token| token.id}.join(',')}</input_token_ids>
        <output_token_ids>#{output_tokens.collect{|token| token.id}.join(',')}</output_token_ids>
      </mode>
      XML
      
    end
    
    private
    
    #finds preceding transition modes by identifying creators of all input_tokens
    def find_preceding_modes
      modes = []
      input_tokens.each do |token|
        token = PNEngine::Token.find(token.id) #dirty fix for weird activerecord bug
        modes << token.creator_transition_mode
      end
      return modes.uniq
    end
    
  end #class
end #module
